package com.lin.manager.tool.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lin.manager.common.ex.BizException;
import com.lin.manager.common.req.QueryPage;
import com.lin.manager.common.utils.PageUtil;
import com.lin.manager.common.utils.StrUtils;
import com.lin.manager.tool.dao.GeneratorDbDao;
import com.lin.manager.tool.entity.ColumnInfo;
import com.lin.manager.tool.entity.GenColumn;
import com.lin.manager.tool.entity.GenTable;
import com.lin.manager.tool.entity.TableInfo;
import com.lin.manager.tool.entity.dto.TableDto;
import com.lin.manager.tool.model.TableParamVo;
import com.lin.manager.tool.model.TableVo;
import com.lin.manager.tool.service.GeneratorDbService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author linjiang
 * @date 2024/5/15
 * @fn
 */
@Service
public class GeneratorDbServiceImpl implements GeneratorDbService {
    private static Map<String, String> typeMap = new HashMap<>();

    {
        typeMap.put("tinyint", "Integer");
        typeMap.put("int", "Integer");
        typeMap.put("varchar", "String");
        typeMap.put("text", "String");
        typeMap.put("datetime", "Date");
        typeMap.put("date", "Date");
        typeMap.put("bigint", "Long");
        typeMap.put("decimal", "BigDecimal");
    }

    @Value("${gen.author:linjiang}")
    private String author;
    @Value("${gen.package:com.lin}")
    private String packageName;
    @Value("${gen.module:manager}")
    private String module;

    @Autowired
    private GeneratorDbDao generatorDbDao;

    @Override
    public PageUtil selectTableFromDb(TableParamVo tableParamVo) {
        Page<TableInfo> tableInfoPage = generatorDbDao.selectTableFromDb(new Page(tableParamVo.getPageNum(), tableParamVo.getPageSize()), tableParamVo.getDb());
        return new PageUtil(tableInfoPage);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void importTableToSystem(GenTable genTable) {
        validate(genTable);

        String tableName = genTable.getTableName();
        String className = StrUtils.toBigCamel(tableName, "_");
        genTable.setClassName(className);
        genTable.setAuthor(author);
        genTable.setPackageName(packageName);
        genTable.setBizName(className);
        genTable.setModule(module);

        generatorDbDao.insertGenTable(genTable);

        // 插入数据表的字段。
        List<ColumnInfo> columnInfos = this.selectTableFieldByDbTable(genTable.getDb(), tableName);

        for (ColumnInfo columnInfo : columnInfos) {
            GenColumn genColumn = GenColumn.builder()
                    .genTableId(genTable.getId())
                    .columnName(columnInfo.getColumnName())
                    .columnComment(columnInfo.getColumnComment())
                    .columnType(columnInfo.getColumnType())
                    .javaProperty(StrUtils.toSmallCamel(columnInfo.getColumnName(), "_"))
                    .javaType(getJavaType(columnInfo.getColumnType()))
                    .build();

            generatorDbDao.insertGenColumn(genColumn);
        }
    }

    @Override
    public PageUtil selectTableFromSystem(QueryPage page) {
        Page<GenTable> tablePage = generatorDbDao.selectTableFromSystem(new Page(page.getPageNum(), page.getPageSize()), page.getKey());
        return new PageUtil(tablePage);
    }

    @Override
    public void deleteTableFromSystem(List<Integer> ids) {
        Assert.notNull(ids, "请选择需要删除的业务表");
        generatorDbDao.deleteTableFromSystem(ids);
        generatorDbDao.deleteTableFieldFromSystem(ids);
    }

    @Override
    public TableVo selectTableInfoById(Integer id) {
        TableDto tableDto = generatorDbDao.selectTableInfoById(id);

        TableVo tableVo = new TableVo();
        BeanUtils.copyProperties(tableDto, tableVo);
        return tableVo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateGenTable(TableVo vo) {
        GenTable genTable = new GenTable();
        BeanUtils.copyProperties(vo, genTable);
        generatorDbDao.updateGenTable(genTable);

        generatorDbDao.deleteTableFieldFromSystem(Collections.singletonList(vo.getId()));

        for (GenColumn genColumn : vo.getGenColumns()) {
            validate(genColumn);

            genColumn.setId(null);
            genColumn.setGenTableId(vo.getId());
            generatorDbDao.insertGenColumn(genColumn);
        }
    }

    private void validate(GenTable genTable) {
        if (StringUtils.isEmpty(genTable.getDb())) {
            throw new BizException("请选择数据库");
        }
        if (StringUtils.isEmpty(genTable.getTableName())) {
            throw new BizException("导入的表不能为空");
        }
        Integer count = generatorDbDao.selectCountByTableName(genTable.getTableName());
        if (count > 0) {
            throw new BizException("导入的表已存在，请重新选择");
        }
    }

    private void validate(GenColumn genColumn) {
        if (StringUtils.isEmpty(genColumn.getJavaProperty())) {
            throw new BizException("java属性不能为空");
        }
        if (StringUtils.isEmpty(genColumn.getJavaType())) {
            throw new BizException("java类型不能为空");
        }
    }

    public String getJavaType(String columnType) {
        if (columnType == null) {
            return "";
        }

        AtomicReference<String> javaType = new AtomicReference<>("");
        typeMap.forEach((k, v) -> {
            if (columnType.startsWith(k)) {
                javaType.set(v);
            }
        });

        return javaType.get();
    }

    /**
     * 查询数据库字段
     *
     * @return
     */
    public List<ColumnInfo> selectTableFieldByDbTable(String db, String table) {
        return generatorDbDao.selectTableFieldByDbTable(db, table);
    }

}
